import os

from datetime import datetime
from functools import total_ordering

import lib, sql

from xdh_logging import initLog
from settings import settings
from nothing import Nothing
from tags import tagged_file, get_mod, get_length, get_tag

log = initLog("track")

@total_ordering
class Played(object):
	def __init__(self, track, data):
		self.track = track
		self._played = []

		if data != None:
			for entry in data:
				self._played.append(entry.played)


	def __getitem__(self, index):
		return self._played[index]


	def append(self, value):
		self._played.append(value)

		data = [{"filename": self.track.filename, "played": value}]

		sql.process(sql.TrackPlayedData, "insert", data)


	def index(self, value):
		return self._played.index(value)


	def count(self, value):
		return self._played.count(value)


	def __repr__(self):
		return repr(self._played)


	def __str__(self):
		return lib.safe_str(self._played)


	def __unicode__(self):
		return lib.safe_unicode(self._played)


	def __eq__(self, value):
		return self._played == value


	def __ne__(self, value):
		return self._played != value


	def __lt__(self, value):
		return self._played < value


	def __len__(self):
		return len(self._played)


	def __contains__(self, value):
		return value in self._played

	def __iter__(self):
		return iter(self._played)


@tagged_file(excludes = ["category", "flags", "length", "modified"]) # Excluding the pieces we're loading into the DB. These will be manually created below.
@total_ordering
class Track(object):
	"""
	Class that handles tracks in the playlist generator. The tracks are defined
	using the path to the music file, and then it relies on the tagging mixins defined
	above to load the correct tags for the given file type. It also relies on the SQLAlchemy
	classes defined above to handle the added & played dates. This way, a Track object
	actually does not store any data within it other than the filename for the track object.
	Everything else is retrieved from other sources.
	"""

	def __init__(self, filename, test = False):
		"""
		Constructs a track based on the given data file.
		There really is only two "hidden" attributes that are loaded,
		using the fix_filename() class method defined earlier.
		Everything else in this class instance is defined throuogh the mixin,
		or a couple property methods.
		"""


		if isinstance(filename, sql.TrackData):
			self.modified = filename.modified
			self.category = filename.category
			self.added = filename.added
			self.flags = filename.flags
			self.length = filename.length
			self.played = filename.played
			self.path = filename.path
			filename = filename.filename

		elif isinstance(filename, Track):
			if hasattr(filename, "_modified"):
				self._modified = filename._modified
				self._category = filename._category
				self._added = filename._added
				self._flags = filename._flags
				self._length = filename._length
				self._played = filename._played

			filename = filename.filename

		elif "filename" in dir(filename):
			filename = filename.filename

		self._test = test
		self._filename = lib.fix_filename(filename, self._test, reverse = True)


	@property
	def ext(self):
		if not hasattr(self, "_ext"):
			self._ext = settings.re.music_ext.search(self.filename.lower()).group(1)

		return self._ext

	def _load_db(self):
		found = False
		for results in sql.TrackData.get_track_data(self.filename):
			log.debug(u"Loading DB settings for {filename}".format(filename = lib.safe_unicode(self.filename)))
			if results != None:
				found = True
				self.added = results.added
				self.category = results.category
				self.modified = results.modified
				self.flags = results.flags
				self._length = results.length
				self.played = results.played
				self.path = results.path
                        
		if not found:
			added = datetime.today()
			category = get_tag("category", self.filename, self._test)
			modified = get_mod(self.filename, self._test)
			length = get_length(self.filename, self._test)
			path = lib.fix_filename(os.path.dirname(lib.fix_filename(self.filename)), reverse = True)

			data = [{"filename": self.filename, "path": path, "added": added, "modified": modified, "category": category, "length": length}]
			flags = [{"filename": self.filename, "flag": flag} for flag in get_tag("flags", self.filename, self._test)]

			sql.process(sql.TrackData, "insert", data)
			sql.process(sql.TrackFlagData, "insert", flags)

			self.path = path
			self.added = added
			self.category = category
			self.modified = modified
			self.flags = flags
			self._length = length
			self.played = None


	@property
	def added(self):
		if not hasattr(self, "_added"):
			self._load_db()

		return self._added


	@added.setter
	def added(self, added):
		self._added = added
        

	@property
	def length(self):
		"""
		Returns the length of the track (in seconds).
		"""
		if not hasattr(self, "_length"):
			self._load_db()

		return self._length


	@length.setter
	def length(self, length):
		self._length = length


	def __len__(self):
		return self.length


	@property
	def path(self):
		if not hasattr(self, "_path"):
			self._load_db()

		return self._path


	@path.setter
	def path(self, path):
		self._path = path
        

	@property
	def modified(self):
		if not hasattr(self, "_modified"):
			self._load_db()

		return self._modified


	@modified.setter
	def modified(self, modified):
		self._modified = modified
        

	@property
	def category(self):
		if not hasattr(self, "_category"):
			self._load_db()

		return self._category
        

	@category.setter
	def category(self, category):
		self._category = category


	@property
	def flags(self):
		if not hasattr(self, "_flags"):
			self._load_db()

		return self._flags

	@flags.setter
	def flags(self, data):
		temp = set()
		for entry in data:
			if hasattr(entry, "flag"):
				temp.add(entry.flag)
			elif "flag" in entry:
				temp.add(entry["flag"])
			else:
				temp.add(entry)

		self._flags = set(temp)


	@property
	def played(self):
		if not hasattr(self, "_played"):
			self._load_db()
		return self._played


	@played.setter
	def played(self, played_data):
		self._played = Played(self, played_data)


	def __eq__(self, other):
		if 'filename' not in dir(other):
			return self.filename == lib.safe_unicode(other)
		return self.filename == other.filename


	def __ne__(self, other):
		if 'filename' not in dir(other):
			return self.filename != lib.safe_unicode(other)
		return self.filename != other.filename


	def __lt__(self, other):
		if 'filename' not in dir(other):
			return self.filename < lib.safe_unicode(other)
		return self.filename < other.filename


	@property
	def filename(self):
		"""
		Returns the filename & path for this track, relative to the settings.path.music_base setting.
		"""
		return lib.safe_unicode(self._filename)

	@property
	def favorite(self):
		if settings.re.split_tag.search(getattr(settings.tags, settings.favorite.tag.name).type) is not None:
			return settings.favorite.tag.value in getattr(self, settings.favorite.tag.name)
		else:
			return settings.favorite.tag.value == getattr(self, settings.favorite.tag.name)

	def __str__(self):
		return lib.safe_str(self.__unicode__())

	def __unicode__(self):
		ext = settings.re.music_ext.search(self.filename.lower()).group(1)

		ret = {}
		for tag in getattr(settings.types, ext).tags.__dict__:
			ret[tag] = lib.safe_unicode(getattr(self, tag))

		return lib.safe_unicode(settings.data.track_format).format(**ret)

	def __repr__(self):
		if hasattr(self, "_filename"):
			return repr(lib.safe_unicode(self.filename))
		else:
			return "<Track()>"

	def __hash__(self):
		return self.filename.__hash__()

if __name__ == "__main__":
	pass
